#pragma once
#include "..\typedefs\typedefs.h"
#include "..\macros\assertions.h"

namespace NEngine {

// ================================================================================================
// A Basic dynamic array
// ================================================================================================
template<typename T> class CDynamicArray {

    private:

        T* arr;
        uint length;
        uint cursize;

        // -- grow the array by 2 * length
        void Grow();
    public:

        // -- default constructor
        CDynamicArray();

        // -- copy constructor
        CDynamicArray(const CDynamicArray<T>& dynarr);

        // -- destructor
        ~CDynamicArray();

        // -- public methods
        void Append(const T& value);

        // -- accessor methods
        uint GetSize();
        uint GetSize() const;
        uint GetLength();
        uint GetLength() const;
        T& GetLast(); 
        const T& GetLast() const;

        // -- operator overloads
        T& operator[](const uint i);
        const T& operator[](const uint i) const;
        CDynamicArray<T> operator=(const CDynamicArray& dynarr);
};


// ================================================================================================
// Constructors
// ================================================================================================
template<typename T> inline CDynamicArray<T>::CDynamicArray() 
                                         : length(10), cursize(0), arr(new T[10]) {
}

// ================================================================================================
// Copy constructors
// ================================================================================================
template<typename T> inline CDynamicArray<T>::CDynamicArray(const CDynamicArray<T>& dynarr) {
    length = dynarr.GetLength();
    cursize = dynarr.GetSize();
    arr = new T[length];
    for(uint i = 0; i < dynarr.GetSize(); ++i) 
        arr[i] = dynarr[i];
}

// ================================================================================================
// Destructor
// ================================================================================================
template<typename T> inline CDynamicArray<T>::~CDynamicArray() {
    if(arr)
        delete [] arr;
    arr = nullptr;
}

// ================================================================================================
// Append value to the end of the array
// ================================================================================================
template<typename T> inline void CDynamicArray<T>::Append(const T& value) {
    if(cursize == length) 
        Grow();

    arr[cursize++] = value;
}

// ================================================================================================
// Grow the array
// ================================================================================================
template<typename T> inline void CDynamicArray<T>::Grow() {
   T* temp = arr;
   arr = new T[length * 2];
   for(uint i = 0; i < length; ++i) 
       arr[i] = temp[i];

   length *= 2;
   delete [] temp;
}

// ================================================================================================
// Get the size of the array
// ================================================================================================
template<typename T> inline uint CDynamicArray<T>::GetSize() {
    return cursize;
}

template<typename T> inline uint CDynamicArray<T>::GetSize() const {
    return cursize;
}

// ================================================================================================
// Get the length of the array
// ================================================================================================
template<typename T> inline uint CDynamicArray<T>::GetLength() {
    return length;
}

template<typename T> inline uint CDynamicArray<T>::GetLength() const {
    return length;
}

template<typename T> inline T& CDynamicArray<T>::GetLast() {
    ASSERT_(cursize, "No elements in the array");
    return arr[cursize-1];
}

template<typename T> inline const T& CDynamicArray<T>::GetLast() const {
    ASSERT_(cursize, "No elements in the array");
    return arr[cursize-1];
}

// ================================================================================================
// Operator overloads
// ================================================================================================
template<typename T> 
inline T& CDynamicArray<T>::operator[](const uint i) {
    return arr[i];
}

template<typename T>
inline const T& CDynamicArray<T>::operator[](const uint i) const {
    return arr[i];
}

template<typename T>
inline CDynamicArray<T> CDynamicArray<T>::operator=(const CDynamicArray<T>& dynarr) {
    delete [] arr;
    length = dynarr.GetLength();
    cursize = dynarr.GetSize();
    arr = new T[length];
    for(uint i = 0; i < dynarr.GetSize(); ++i) 
        arr[i] = dynarr[i];

    return *this;    
}

} // namespace NEngine
